<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        canvas {
            /* background-color: black; */
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="LightTextureVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_PvM4;
    uniform mat4 u_ModelM4;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
    }
</script>
<script id="LightTextureFS" type="x-shader/x-fragment">
    precision mediump float;
    void main(){
        //提高精度(解决不了根本问题还是不能太近或者太远)
        const vec4 bitShift = vec4(
            1.0,
            256.0,
            256.0 * 256.0,
            256.0 * 256.0 * 256.0
        );
        const vec4 bitMask = vec4(vec3(1.0 / 256.0), 0.0);
        vec4 depth = fract(gl_FragCoord.z * bitShift);
        depth -= depth.gbaa * bitMask;
        gl_FragColor = depth;
    }
</script>
<script id="TextureVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_PvM4;
    uniform mat4 u_ModelM4;
    uniform mat4 u_LightPvM4;//灯光的投影矩阵
    varying vec4 v_ClipposLight;//剪贴灯(计算投影)
    void main(){
        vec4 worldPosi = u_ModelM4 * a_Position;
        gl_Position = u_PvM4 * worldPosi;
        v_ClipposLight = u_LightPvM4 * worldPosi;
    }
</script>
<script id="TextureFS" type="x-shader/x-fragment">
    precision mediump float;
    varying vec4 v_ClipposLight;//剪贴灯(计算投影)
    uniform sampler2D u_ShadowMap;//阴影贴图
    bool isInShadow(){
        //将世界坐标转为裁剪坐标
        vec3 fragPos = (v_ClipposLight.xyz / v_ClipposLight.w) / 2.0 + 0.5;
        vec4 shadowFrag = texture2D(u_ShadowMap, fragPos.xy);
        const vec4 bitShift = vec4(
            1.0,
            1.0 / (256.0),
            1.0 / (256.0 * 256.0),
            1.0 / (256.0 * 256.0 * 256.0)
        );
        float depth = dot(shadowFrag, bitShift);
        return fragPos.z > depth + 1.0 / (256.0 * 4.0);//最后的+数是调精度
    }
    void main(){
        float darkness = isInShadow() ? 0.7 : 1.0;
        gl_FragColor = vec4(vec3(darkness), 1.0);
        //gl_FragColor = vec4(1.0,1.0,1.0,1.0);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Poly from '../js/Poly4.js'
    import Control from '../js/Control.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'
    import Frame from '../js/Poly2/Frame.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const gl = canvas.getContext("webgl", { antialias: true, });
    //#endregion

    //#region 
    const lightProgram = CreateProgram(gl,
        document.querySelector('#LightTextureVS').innerText,
        document.querySelector('#LightTextureFS').innerText);

    const program = CreateProgram(gl,
        document.querySelector('#TextureVS').innerText,
        document.querySelector('#TextureFS').innerText);

    const frame = new Frame({ gl });
    const scene = new Scene({ gl });
    frame.regProgram('light', {
        program: lightProgram,
        attribs: ['a_Position'],
        uniforms: ['u_PvM4', 'u_ModelM4'],
    });
    scene.regProgram('texture', {
        program: program,
        attribs: ['a_Position'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_LightPvM4', 'u_ShadowMap'],
    });
    //#endregion

    //灯光
    const light = new PerspectiveCamera(60, canvas.width / canvas.height, 0.1, 10);
    light.position.set(0, 2, 0);
    light.lookAt(0, 0, 0.0);
    light.updateMatrixWorld(true);
    const lightPvM4 = light.projectionMatrix.clone().multiply(light.matrixWorldInverse);

    //摄像机
    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 0.1, 10);
    camera.position.set(1, 1, 1);
    camera.lookAt(0, 0, 0.0);
    camera.updateMatrixWorld(true);
    const cameraPvM4 = camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse);

    //三角形对象
    const triangleH = 0.1;
    const triangle = [
        -0.1, triangleH, 0.1,
        0.1, triangleH, 0.1,
        0.0, triangleH, -0.1,
    ];

    const floorH = 0.0;
    //投影的平面对象
    const floor = [
        -0.2, 0.0, 0.2,
        0.2, 0.0, 0.2,
        -0.2, 0.0, -0.2,
        0.2, 0.0, -0.2,
    ];


    //main
    {
        //创建贴图
        const texture = createTetxure();

        //创建对象
        createObj3D(texture);
    }

    function createTetxure() {
        const triangleTexture = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(triangle) },
                }
            }),
            mat: new Mat({
                program: 'light',
                mode: ['TRIANGLE_STRIP'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: lightPvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                }
            })
        });
        const floorTexture = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(floor) },
                }
            }),
            mat: new Mat({
                program: 'light',
                mode: ['TRIANGLE_STRIP'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: lightPvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                }
            })
        });

        frame.add(triangleTexture);
        frame.add(floorTexture);
        frame.draw();

        return frame.texture;
    }

    function createObj3D(texture) {
        const triangleObj = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(triangle) },
                }
            }),
            mat: new Mat({
                program: 'texture',
                mode: ['TRIANGLE_STRIP'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: cameraPvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_LightPvM4': { type: 'uniformMatrix4fv', value: lightPvM4.elements },
                },
                maps: { 'u_ShadowMap': { texture: texture } }
            })
        });
        const floorObj = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(floor) },
                }
            }),
            mat: new Mat({
                program: 'texture',
                mode: ['TRIANGLE_STRIP'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: cameraPvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_LightPvM4': { type: 'uniformMatrix4fv', value: lightPvM4.elements },
                },
                maps: { 'u_ShadowMap': { texture: texture } }
            })
        });
        scene.add(triangleObj);
        scene.add(floorObj);

        scene.draw();
    }




</script>

</html>